home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / programming / c / metre.lha / rules.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-07  |  7.2 KB  |  189 lines

  1. /*************************************************************
  2.    Copyright (c) 1993,1994 by Paul Long  All rights reserved.
  3. **************************************************************/
  4.  
  5. /*************************************************************
  6.    rules.c -   This source file contains a typical set of
  7.                rules in the rules() function that define
  8.                a simple metrics tool.
  9. **************************************************************/
  10.  
  11.  
  12. #include <limits.h>
  13. #include "metre.h"
  14.  
  15. /*
  16.    In addition to the parser's command-line options, e.g., -D, the rules look
  17.    for a -Q option to suppress its verbose key and explanation that it
  18.    otherwise generates at the beginning of the output.
  19. */
  20. #define QUIET_OPT_CHAR        'Q'
  21.  
  22. /* Defines for warnings that are generated by the rules, not the parser. */
  23. #define W_GOTO                0, "goto statement used (%u time%s)"
  24. #define W_CONTINUE            1, "continue statement used (%u time%s)"
  25. #define W_RETURNS             2, "Multiple return statements (%u)"
  26. #define W_TAB_SPACE_INDENT    3, "Tab & space used on same line for indention"
  27. #define W_MULT_STATEMENTS     4, "Multiple statements (%u) per line"
  28. #define W_NONSTANDARD         5, "Non-standard character (0x%x) encountered"
  29.  
  30. /* Nicety to evaluate as "s" if argument is greater than one; otherwise, "". */
  31. #define NUMBER(x)       ((x) > 1 ? "s" : "")
  32.  
  33. /* Assign max value to integer argument regardless of type, e.g., long int. */
  34. #define TYPE_MAX(x) ((x = ~0) < 0 ? x = ~(1 << sizeof x * CHAR_BIT - 1) : x)
  35.  
  36. /*
  37.    The rules maintain their function statistics in this structure.
  38.    Could have also been just a bunch of separate variables, though.
  39. */
  40. static struct {
  41.    struct {
  42.       unsigned min, max;
  43.    } decisions;            /* Min and max binary decision points in function. */
  44.    struct {
  45.       unsigned min, max;   /* Min and max lines in a function. */
  46.    } lines;
  47. } per_func;
  48.  
  49. static BOOLEAN mixed_indent_lines;     /* Whether spaces _&_ tabs in indent. */
  50. static unsigned mod_statements;        /* Total statements in module (file). */
  51. static unsigned mod_func_lines;        /* Total function lines in module. */
  52. static unsigned fcn_gotos;             /* How many gotos in a function. */
  53. static unsigned fcn_continues;         /* How many continues in a function. */
  54. static unsigned fcn_returns;           /* How many returns in a function. */
  55. static unsigned fcn_depth_max;         /* Maximum nesting level in a function.*/
  56.  
  57. void rules(void)
  58. {
  59.    /*
  60.       If at the beginning of a project and the quiet option has not been
  61.       specified, print the key and explanation.
  62.    */
  63.    if (prj.begin && !option(QUIET_OPT_CHAR))
  64.    {
  65.       fprintf(out_fp, "Explanation and Guidelines:\n");
  66.       fprintf(out_fp, "<          Minimum\n");
  67.       fprintf(out_fp, ">          Maximum\n");
  68.       fprintf(out_fp, "~          Average\n");
  69.       fprintf(out_fp, "(nothing)  Total\n");
  70.       fprintf(out_fp, "CCI        McCabe's Cyclomatic Complexity Index.  Number of binary\n");
  71.       fprintf(out_fp, "           decision points plus one.  Should be less than 10.\n");
  72.       fprintf(out_fp, "Depth      Maximum control structure depth.  Should be less than 6.\n\n");
  73.       fprintf(out_fp, "A goto probably indicates that there should be another function.  Elses\n");
  74.       fprintf(out_fp, "are probably a better solution than multiple return statements.  An else\n");
  75.       fprintf(out_fp, "is probably a better solution than a continue statement.  A good size\n");
  76.       fprintf(out_fp, "for a module is 30 lines or so with an upper limit of 60 lines.  One\n");
  77.       fprintf(out_fp, "statement per line.  Don't mix tabs and spaces for indention.\n\n");
  78.    }
  79.  
  80.    /* At the beginning of the module, initialize variables. */
  81.    if (mod.begin)
  82.    {
  83.       per_func.decisions.max = 0;
  84.       TYPE_MAX(per_func.decisions.min);
  85.       per_func.lines.max = 0;
  86.       TYPE_MAX(per_func.lines.min);
  87.       mod_func_lines = 0;
  88.       mod_statements = 0;
  89.       mixed_indent_lines = FALSE;
  90.    }
  91.  
  92.    /* At the end of the module, print a module summary. */
  93.    if (mod.end)
  94.    {
  95.       fprintf(out_fp, "\n\nModule Summary\n");
  96.       if (mod.functions != 0)
  97.       {
  98.          fprintf(out_fp, "   CCI:             <%u >%u ~%u\n",
  99.                per_func.decisions.min + 1, per_func.decisions.max + 1,
  100.                mod.decisions / mod.functions + 1);
  101.          fprintf(out_fp, "   Lines:           <%u >%u ~%u %u\n",
  102.                per_func.lines.min, per_func.lines.max,
  103.                mod_func_lines / mod.functions, mod.lines.total);
  104.       }
  105.       fprintf(out_fp, "     Exec:          %u\n", mod.lines.exec);
  106.       fprintf(out_fp, "     Comment:       %u\n", mod.lines.com);
  107.       fprintf(out_fp, "     White:         %u\n", mod.lines.white);
  108.       fprintf(out_fp, "   Statements:      %u\n", mod_statements);
  109.       fprintf(out_fp, "   Functions:       %u\n", mod.functions);
  110.  
  111.       if (mixed_indent_lines)
  112.          warn(W_TAB_SPACE_INDENT);
  113.    }
  114.  
  115.    /* At the beginning of each function, initialize variables. */
  116.    if (fcn.begin)
  117.    {
  118.       fcn_gotos = 0;
  119.       fcn_continues = 0;
  120.       fcn_returns = 0;
  121.       fcn_depth_max = 0;
  122.    }
  123.  
  124.    /*
  125.       At the end of each function, set module-wide statistics variables
  126.       and print a function summary.
  127.    */
  128.    if (fcn.end)
  129.    {
  130.       if (fcn.decisions > per_func.decisions.max)
  131.             per_func.decisions.max = fcn.decisions;
  132.       if (fcn.decisions < per_func.decisions.min)
  133.             per_func.decisions.min = fcn.decisions;
  134.  
  135.       if (fcn.lines.total > per_func.lines.max)
  136.             per_func.lines.max = fcn.lines.total;
  137.       if (fcn.lines.total < per_func.lines.min)
  138.             per_func.lines.min = fcn.lines.total;
  139.  
  140.       mod_func_lines += fcn.lines.total;
  141.       mod_statements += fcn.low + fcn.high;
  142.  
  143.       fprintf(out_fp, "\n%s()\n", fcn_name());
  144.       fprintf(out_fp, "   CCI:             %u\n", fcn.decisions + 1);
  145.       fprintf(out_fp, "   Lines:           %u\n", fcn.lines.total);
  146.       fprintf(out_fp, "     Exec:          %u\n", fcn.lines.exec);
  147.       fprintf(out_fp, "     Comment:       %u\n", fcn.lines.com);
  148.       fprintf(out_fp, "     White:         %u\n", fcn.lines.white);
  149.       fprintf(out_fp, "   Statements:      %u\n", fcn.low + fcn.high);
  150.       fprintf(out_fp, "   Depth:           >%u\n", fcn_depth_max);
  151.       if (fcn_gotos > 0)
  152.          warn(W_GOTO, fcn_gotos, NUMBER(fcn_gotos));
  153.       if (fcn_continues > 0)
  154.          warn(W_CONTINUE, fcn_continues, NUMBER(fcn_continues));
  155.       if (fcn_returns > 1)
  156.          warn(W_RETURNS, fcn_returns);
  157.    }
  158.  
  159.    /* At the end of each statement, determine if greatest depth yet reached. */
  160.    if (stm.end)
  161.       if (stm.depth > fcn_depth_max)
  162.          fcn_depth_max = stm.depth;
  163.  
  164.    /* Remember if line found with mixed indention (spaces and tabs). */
  165.    if (lin.is_mixed_indent)
  166.       mixed_indent_lines = TRUE;
  167.  
  168.    /* Print warning if a line contains more than one statement. */
  169.    if (lin.statements > 0)
  170.       warn(W_MULT_STATEMENTS, lin.statements + 1);
  171.  
  172.    /* Count gotos, continues, and returns. */
  173.    if (keyword("goto"))
  174.       ++fcn_gotos;
  175.  
  176.    if (keyword("continue"))
  177.       ++fcn_continues;
  178.  
  179.    if (keyword("return"))
  180.       ++fcn_returns;
  181.  
  182.    /*
  183.       If character encountered that is not in Standard C's source
  184.       character set, print warning.
  185.    */
  186.    if (lex.nonstandard)
  187.       warn(W_NONSTANDARD, lex.nonstandard);
  188. }
  189.